package edu.corn.statemachine.core;

import cn.hutool.core.lang.Pair;
import edu.corn.common.util.AssertUtil;
import edu.corn.statemachine.core.builder.AbstractStateMachineBuilder;
import edu.corn.statemachine.core.builder.Trigger;
import edu.corn.statemachine.core.context.IStateContext;
import edu.corn.statemachine.core.enums.StateMachineEnum;
import edu.corn.statemachine.exception.StateMachineException;
import edu.corn.statemachine.lock.LockPool;


import java.util.HashMap;

import static edu.corn.statemachine.core.enums.StateMachineEnum.STATE_HAS_CHANGED;

public class DefaultSyncStateMachine<S extends Enum<S>, E extends Enum<E>> implements StateMachine<S, E> {

    /**
     * 一个源状态和一个事件应该能够唯一的确定一个目标状态和触发事件。
     */
    protected final HashMap<Pair<S, E>, Pair<S, Action<S, E>>> actionsMap = new HashMap<>();

    /**
     * 锁池提供线程安全。使状态机不会处于一个中间状态。要么转换成功，要么转换失败。
     */
    private LockPool lock;

    public DefaultSyncStateMachine() {

    }

    public void init(LockPool lock) {
        this.lock = lock;
    }

    @Override
    public void sendEvent(IStateContext<S, E> context) {
        sendEvent(null, context);
    }

    @Override
    public void sendEvent(S sourceState, IStateContext<S, E> context) {
        E event = context.getEvent();
        AssertUtil.requireNonNull(event, e -> {
            throw new StateMachineException(StateMachineEnum.EVENT_NULL);
        });

        Pair<S, E> trigger = new Pair<>(sourceState, event);
        Pair<S, Action<S, E>> state2actionPair = actionsMap.get(trigger);

        AssertUtil.requireNonNull(state2actionPair, e -> {
            throw new StateMachineException(StateMachineEnum.UNSUPPORTED_OPERATION);
        });
        AssertUtil.requireNonNull(state2actionPair.getKey(), e -> {
            throw new StateMachineException(StateMachineEnum.TARGET_NULL);
        });

        Action<S, E> action = state2actionPair.getValue();
        // 拿到锁后执行，不保证一定执行成功。
        lock.tryLock(context.getSubject());
        try {
            // 验证是否是源状态。
            if (context.getSubject() != null && context.getSubject().getState().equals(sourceState)) {
                throw new StateMachineException(STATE_HAS_CHANGED);
            }

            if (action != null) {
                action.execute(context);
            }


            // 不存在没有对象的情况。成功的话设置状态流转。
            context.getSubject().setState(state2actionPair.getKey());
        } finally {
            lock.unlock(context.getSubject());
        }
    }

    public static class StateMachineBuilder<S extends Enum<S>, E extends Enum<E>> extends AbstractStateMachineBuilder<S, E> {

        @Override
        public StateMachine<S, E> build() {
            DefaultSyncStateMachine<S, E> stateMachine = new DefaultSyncStateMachine<S, E>();

            for (Trigger<S, E> trigger : triggerBuilders) {
                S sourceState = trigger.getSourceState();
                S targetState = trigger.getTargetState();
                E eventType = trigger.getEventType();
                Action<S, E> action = trigger.getAction();
                stateMachine.actionsMap.put(new Pair<>(sourceState, eventType), new Pair<>(targetState, action));
            }

            return stateMachine;
        }

    }
}
